Text provided under a Creative Commons Attribution license, CC-BY. Code under MIT license. (c)2014 Lorena A. Barba, Olivier Mesnard. Thanks: NSF for support via CAREER award #1149784.
Version 0.2 -- February 2014

Vortex

Let's recap. In the first three lessons of AeroPython, you computed:

  1. a source-sink pair—you learned how to make a streamplot() and use scatter() to mark the location of the singular points; that lesson also showed a bad way to get the velocity values, i.e., iterating over all points on the grid.

  2. a source-sink pair in a free stream, creating a Rankine oval—here, you learned to update all values of velocity in one line, the Python way! You also learned to define custom functions, to make Python work for you and look even more like plain English.

  3. a doublet—this one is wonderful, because the stream-line pattern gives the flow around a circular cylinder (in two dimensions). You encountered the D'Alembert paradox and hopefully that got you thinking. (If not, go back to that lesson and think!)

In this fourth lesson, we compute the flow of a vortex. Let's get started

What's a vortex?

This question is deeper than you might think! The simple answer is that a vortex is motion in circular streamlines. Imagine streamlines that are concentric circles about a given point—what's confusing is that this does not mean that fluid elements are themselves rotating!

In an irrotational vortex, the tangential velocity is constant along a (circular) streamline and inversely proportional to the radius. In polar coordinates,

$$u_r\left(r,\theta\right) = 0 \quad \text{,} \quad u_\theta\left(r,\theta\right) = \frac{\text{constant}}{r}$$

The vorticity is zero everywhere, except at the location of the point vortex, where it is infinite.

We reminded you about circulation in the first lesson (Source & Sink). Let's use that. Around any circular streamline enclosing the vortex, and using the sign convention that a negative vortex circulates anti-clockwise, we have:

$$\Gamma = -\oint \mathbf{v}\cdot d\vec{l} = -u_\theta 2 \pi r$$

So the constant in the velocity definition is equal to $\Gamma/2\pi$, and

$$u_\theta\left(r,\theta\right) = \frac{\Gamma}{2\pi r}$$

We can get the stream function by integrating the velocity components:

$$\psi\left(r,\theta\right) = \frac{\Gamma}{2\pi}\ln r$$

In Cartesian coordinates, the stream function is

$$\psi\left(x,y\right) = \frac{\Gamma}{4\pi}\ln\left(x^2+y^2\right)$$

while the velocity components are

$$u\left(x,y\right) = \frac{\Gamma}{2\pi}\frac{y}{x^2+y^2} \qquad v\left(x,y\right) = -\frac{\Gamma}{2\pi}\frac{x}{x^2+y^2}$$

Let's compute a vortex

The set-up is the same as before: we load our favorite libraries, and we create a grid of points to evaluate the velocity field.


In [1]:
import numpy
import math
from matplotlib import pyplot

In [2]:
N = 50                                # Number of points in each direction
x_start, x_end = -2.0, 2.0            # x-direction boundaries
y_start, y_end = -1.0, 1.0            # y-direction boundaries
x = numpy.linspace(x_start, x_end, N)    # computes a 1D-array for x
y = numpy.linspace(y_start, y_end, N)    # computes a 1D-array for y
X, Y = numpy.meshgrid(x, y)              # generates a mesh grid

Give your vortex a of strength $\Gamma=5$ and place it at the center of your domain:


In [3]:
gamma = 5.0                      # strength of the vortex
x_vortex, y_vortex = 0.0, 0.0    # location of the vortex

We will define two functions, get_velocity_vortex() and get_stream_function_vortex(), to compute the velocity components and the stream function on our Cartesian grid, given the strength and the location of the vortex. Then we use our vortex functions to evaluate everything on the grid points:


In [4]:
def get_velocity_vortex(strength, xv, yv, X, Y):
    """Returns the velocity field generated by a vortex.
    
    Arguments
    ---------
    strength -- strength of the vortex.
    xv, yv -- coordinates of the vortex.
    X, Y -- mesh grid.
    """
    u = + strength/(2*math.pi)*(Y-yv)/((X-xv)**2+(Y-yv)**2)
    v = - strength/(2*math.pi)*(X-xv)/((X-xv)**2+(Y-yv)**2)
    
    return u, v

In [5]:
def get_stream_function_vortex(strength, xv, yv, X, Y):
    """Returns the stream-function generated by a vortex.
    
    Arguments
    ---------
    strength -- strength of the vortex.
    xv, yv -- coordinates of the vortex.
    X, Y -- mesh grid.
    """
    psi = strength/(4*math.pi)*numpy.log((X-xv)**2+(Y-yv)**2)
    
    return psi

In [6]:
# computes the velocity field on the mesh grid
u_vortex, v_vortex = get_velocity_vortex(gamma, x_vortex, y_vortex, X, Y)

# computes the stream-function on the mesh grid
psi_vortex = get_stream_function_vortex(gamma, x_vortex, y_vortex, X, Y)

We are now able to visualize the streamlines of a vortex, and they look like concentric circles around the vortex center, as expected.


In [9]:
# plots the streamlines
%matplotlib inline

size = 10
pyplot.figure(figsize=(size, (y_end-y_start)/(x_end-x_start)*size))
pyplot.xlabel('x', fontsize=16)
pyplot.ylabel('y', fontsize=16)
pyplot.xlim(x_start, x_end)
pyplot.ylim(y_start, y_end)
pyplot.streamplot(X, Y, u_vortex, v_vortex, density=2, linewidth=1, arrowsize=1, arrowstyle='->')
pyplot.scatter(x_vortex, y_vortex, color='#CD2305', s=80, marker='o');


Vortex & Sink

For fun, let's use our superposition powers. Add a vortex to a sink, using two new functions to compute the velocity components and the stream function of the sink, and adding to those of a vortex (remember that the sink can be easily replaced by a source by just changing the sign of the strength).


In [8]:
strength_sink = -1.0            # strength of the sink
x_sink, y_sink = 0.0, 0.0       # location of the sink

In [9]:
def get_velocity_sink(strength, xs, ys, X, Y):
    """Returns the velocity field generated by a sink.
    
    Arguments
    ---------
    strength -- strength of the sink.
    xs, ys -- coordinates of the sink.
    X, Y -- mesh grid.
    """
    u = strength/(2*math.pi)*(X-xs)/((X-xs)**2+(Y-ys)**2)
    v = strength/(2*math.pi)*(Y-ys)/((X-xs)**2+(Y-ys)**2)
    
    return u, v

In [10]:
def get_stream_function_sink(strength, xs, ys, X, Y):
    """Returns the stream-function generated by a sink.
    
    Arguments
    ---------
    strength -- strength of the sink.
    xs, ys -- coordinates of the sink.
    X, Y -- mesh grid.
    """
    psi = strength/(2*math.pi)*numpy.arctan2((Y-ys), (X-xs))
    
    return psi

In [11]:
# computes the velocity field on the mesh grid
u_sink, v_sink = get_velocity_sink(strength_sink, x_sink, y_sink, X, Y)

# computes the stream-function on the mesh grid
psi_sink = get_stream_function_sink(strength_sink, x_sink, y_sink, X, Y)

Now, let's visualize the streamlines of the vortex-sink pair, and admire our artistic creation:


In [12]:
# superposition of the sink and the vortex
u = u_vortex + u_sink
v = v_vortex + v_sink
psi = psi_vortex + psi_sink

# plots the streamlines
size = 10
pyplot.figure(figsize=(size, (y_end-y_start)/(x_end-x_start)*size))
pyplot.xlabel('x', fontsize=16)
pyplot.ylabel('y', fontsize=16)
pyplot.xlim(x_start, x_end)
pyplot.ylim(y_start, y_end)
pyplot.streamplot(X, Y, u, v, density=2, linewidth=1, arrowsize=1, arrowstyle='->')
pyplot.scatter(x_vortex, y_vortex, color='#CD2305', s=80, marker='o');


Very cool op-art. But is all this useful? Yes!

First, you will take your training wheels off and—on your own—compute the flow of an infinite row of vortices. Your mission, should you choose to accept it, is in the next notebook.

After that, we will learn about the connection between an vortex, and the force of lift. This turns out to be very important in aerodynamics!


Please ignore the cell below. It just loads our style for the notebook.

In [13]:
from IPython.core.display import HTML
def css_styling():
    styles = open('../styles/custom.css', 'r').read()
    return HTML(styles)
css_styling()


Out[13]: